package com.ccp.dev.system.service;

import com.ccp.dev.core.basic.api.ISysUser;
import com.ccp.dev.core.basic.base.BaseService;
import com.ccp.dev.core.basic.util.ContextUtil;
import com.ccp.dev.core.basic.util.PropertyUtil;
import com.ccp.dev.core.basic.util.UUIDUtils;
import com.ccp.dev.core.basic.web.query.QueryFilter;
import com.ccp.dev.core.util.*;
import com.ccp.dev.system.consts.SecretUtil;
import com.ccp.dev.system.dao.SysFileDao;
import com.ccp.dev.system.dao.SysUserDao;
import com.ccp.dev.system.model.GlobalType;
import com.ccp.dev.system.model.SysFile;
import com.ccp.dev.system.model.SysUser;
import net.sf.jmimemagic.Magic;
import net.sf.jmimemagic.MagicMatch;
import org.apache.commons.io.FileUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.*;

/**
 * SYS_FILE service类
 *
 * @author zhaowj
 */
@Service
public class SysFileService extends BaseService<SysFile> {

	private Log logger = LogFactory.getLog(SysFileService.class);

	@Resource
	private GlobalTypeService globalTypeService;

	@Resource
	private SysTypeKeyService sysTypeKeyService;

	@Resource
	private SysFileDao sysFileDao;

	@Resource
	private SysUserDao sysUserDao;

	/**
	 * 创建文件目录
	 *
	 * @param tempPath
	 * @param fileName 文件名称
	 * @return 文件的完整目录
	 */
	private String createFilePath(String tempPath, String fileName) {
		Calendar cal = Calendar.getInstance();
		// 当前年份
		Integer year = cal.get(Calendar.YEAR);
		// 当前月份
		Integer month = cal.get(Calendar.MONTH) + 1;
		File one = new File(tempPath + File.separator + year + File.separator + month);
		if (!one.exists()) {
			one.mkdirs();
		}
		return one.getPath() + File.separator + fileName;
	}

	/**
	 * 删除文件
	 * 
	 * @param ids id数组
	 */
	@Transactional(rollbackFor = Exception.class)
	public void delFile(String[] ids) {
		if (BeanUtils.isEmpty(ids)) {
			return;
		}
		String attachPath = getBasePath().replace("/", File.separator);
		String saveType = getSaveType();
		// 保存在服务器时才做文件删除
		if (!SysFile.SAVETYPE_DATABASE.equals(saveType)) {
			for (String id : ids) {
				SysFile sysFile = getOneById(id);
				String filePath = sysFile.getFilePath();
				filePath = filePath.replace('\\', '/');
				// 删除文件
				if (StringUtil.isEmpty(attachPath)) {
					filePath = AppUtil.getRealPath(filePath);
				}
				FileUtil.deleteFile(attachPath + File.separator + filePath);
			}
		}
		// 删除数据库中数据（包括文件在数据库的二进制流）
		delByIdArray(ids);
	}

	/**
	 * 文件上传
	 *
	 * @param request    请求
	 * @param typeId     类型id
	 * @param fileFormat 允许上传的文件格式
	 * @param fileLevel  文件密级
	 * @throws Exception
	 */
	@Transactional(rollbackFor = Exception.class)
	public Map<String, Object> fileUpload(MultipartHttpServletRequest request, String typeId, String fileFormat,
			int fileLevel) throws Exception {
		// 获取当前用户的id
		SysUser curUser = (SysUser) ContextUtil.getCurrentUser();
		Map<String, Object> retMap = new HashMap<>(20);

		boolean mark = true;
		// 附件保存路径
		String attachPath = getBasePath();
		// 获取附件类型
		GlobalType globalType = null;
		if (StringUtil.isNotEmpty(typeId)) {
			globalType = globalTypeService.getOneById(typeId);
		}
		Map<String, MultipartFile> files = request.getFileMap();
		Iterator<MultipartFile> it = files.values().iterator();

		List<Map<String, Object>> dataList = new ArrayList<>();
		// 附件保存类型
		String saveType = getSaveType();
		while (it.hasNext()) {
			String fileId = UUIDUtils.getUUIDFor32();
			MultipartFile mf = it.next();
			String oriFileName = mf.getOriginalFilename();
			String extName = FileUtil.getFileExt(oriFileName);
			// 文件格式要求
			if (StringUtil.isNotEmpty(fileFormat)) {
				// 不符合文件格式要求的就标志为false
				if (!(fileFormat.contains("." + extName))) {
					mark = false;
				}
			}
			if (mark) {
				String fileName = fileId + "." + extName;
				// 开始写入物理文件
				String filePath;
				SysFile sysFile = new SysFile();
				filePath = createFilePath(attachPath + File.separator + curUser.getAccount(), fileName);

				Map<String, Object> dataMap = new HashMap<>(20);

				// 开始写入物理文件 二进制流动保存到数据库中
				if (saveType.contains(SysFile.SAVETYPE_DATABASE)) {
					sysFile.setFileBlob(mf.getBytes());
					sysFile.setFilePath("保存到数据库");
					dataMap.put("filePath", "保存到数据库");
				} else {
					// 判断文件是否需要加密
					boolean needCrypt = getFileEncrypt();
					String fileP = filePath.replace(attachPath + File.separator, "");
					if (needCrypt) {
						// 先写文件，再压缩
						FileUtils.copyInputStreamToFile(mf.getInputStream(), new File(filePath));
						String filePassword = getFilePassWord(curUser.getUsername(), fileId);
						ZipUtil.zipSetPass(filePath, true, filePassword, SysFile.ENCRYPT_FILE_SUR_FIX);
						// 需要把原来的文件后缀进行替换
						fileP = fileP.split("\\.")[0] + "." + SysFile.ENCRYPT_FILE_SUR_FIX;
						// 1为加密，0为不加密
						// TODO 旧平台中，后来新增了功能，保存文件的同时，保存一个压缩包，可以参考一下把那个功能加上
						sysFile.setEncrypted(SysFile.ENCRYPTED_YES);
					} else {
						// 不需要加密则直接存储
						FileUtils.copyInputStreamToFile(mf.getInputStream(), new File(filePath));
					}
					// 保存相对路径
					sysFile.setFilePath(fileP);
					dataMap.put("filePath", fileP);
				}
				// 20200515附件表增加密级字段file_level
				sysFile.setFileLevel(fileLevel);
				saveFile(dataMap, sysFile, curUser, fileId, oriFileName, mf, globalType, extName);
				dataList.add(dataMap);
			} else {
				logger.error("文件格式不符合要求！");
				throw new RuntimeException("file.format.warn");
			}
		}
		retMap.put("fileList", dataList);
		return retMap;
	}

	/**
	 * 下载文件
	 * 
	 * @param request  HttpServletRequest
	 * @param response HttpServletResponse
	 * @param fileId   文件id
	 * @throws IOException 抛出异常
	 */
	public void downloadFile(HttpServletRequest request, HttpServletResponse response, String fileId) throws Exception {
		Map<String, Object> map = this.check(fileId);
		if (!"200".equals(map.get("code").toString())) {
			this.setresponse(response, map.get("message").toString());
		} else {
			SysFile sysFile = (SysFile) map.get("sysFile");
			// 获取完整名称
			String fileName = sysFile.getFileName() + "." + sysFile.getExt();
			String saveType = SysFileService.getSaveType();

			String attachPath = SysFileService.getBasePath().replace("/", File.separator);
			// 如果附件保存到数据库中
			if (saveType.equals(SysFile.SAVETYPE_DATABASE) && null != sysFile.getFileBlob()) {
				FileUtil.downLoadFileByByte(request, response, sysFile.getFileBlob(), fileName);
			} else {
				String filePath = sysFile.getFilePath();
//				if (StringUtil.isEmpty(filePath)) {
//					return;
//				}
				if (StringUtil.isEmpty(attachPath)) {
					attachPath = AppUtil.getRealPath("/attachFiles/temp");
				}
				// 如果文件是加密过的
				if (SysFile.ENCRYPTED_YES == sysFile.getEncrypted()) {
					// 原文件路径
					String fullPath = StringUtil.trimSufffix(attachPath, File.separator) + File.separator
							+ filePath.replace("/", File.separator);
					// 暂时解压路径
					String pathToUnZip = StringUtil.trimSufffix(attachPath, File.separator) + File.separator
							+ sysFile.getFileId();
					ZipUtil.unZip(fullPath, pathToUnZip, SysFileService.getFilePassWord(sysFile.getCreator(), fileId));
					// 解压后的文件路径
					String pathAfterUnZip = pathToUnZip + File.separator + sysFile.getFileId() + "." + sysFile.getExt();
					FileUtil.downLoadFile(request, response, pathAfterUnZip, fileName);
					// 是否删除临时文件夹
//                FileUtil.deleteDir(new File(pathToUnZip));
				} else {
					String fullPath = StringUtil.trimSufffix(attachPath, File.separator) + File.separator
							+ filePath.replace("/", File.separator);
					FileUtil.downLoadFile(request, response, fullPath, fileName);
				}
			}
		}
	}
//    public void downloadFile(HttpServletRequest request , HttpServletResponse response ,String fileId) throws Exception {
//        if (StringUtil.isEmpty(fileId)){
//            return;
//        }
//        SysFile sysFile = getOneById(fileId);
//        if (sysFile == null){
//            return;
//        }
////        if(!this.checkLevel(sysFile.getFileLevel())) {
////        	return;
////        }
//        
//        //获取完整名称
//        String fileName = sysFile.getFileName() + "." + sysFile.getExt();
//        String saveType = SysFileService.getSaveType();
//
//        String attachPath = SysFileService.getBasePath().replace("/", File.separator);
//        //如果附件保存到数据库中
//        if (saveType.equals(SysFile.SAVETYPE_DATABASE) && null != sysFile.getFileBlob()) {
//            FileUtil.downLoadFileByByte(request, response, sysFile.getFileBlob(), fileName);
//        } else {
//            String filePath = sysFile.getFilePath();
//            if (StringUtil.isEmpty(filePath)){
//                return;
//            }
//            if (StringUtil.isEmpty(attachPath)) {
//                attachPath = AppUtil.getRealPath("/attachFiles/temp");
//            }
//            //如果文件是加密过的
//            if (SysFile.ENCRYPTED_YES == sysFile.getEncrypted()) {
//                //原文件路径
//                String fullPath = StringUtil.trimSufffix(attachPath, File.separator) + File.separator + filePath.replace("/", File.separator);
//                //暂时解压路径
//                String pathToUnZip = StringUtil.trimSufffix(attachPath, File.separator) + File.separator + sysFile.getFileId();
//                ZipUtil.unZip(fullPath, pathToUnZip, SysFileService.getFilePassWord(sysFile.getCreator(), fileId));
//                //解压后的文件路径
//                String pathAfterUnZip = pathToUnZip + File.separator + sysFile.getFileId() + "." + sysFile.getExt();
//                FileUtil.downLoadFile(request, response, pathAfterUnZip, fileName);
//                //是否删除临时文件夹
////                FileUtil.deleteDir(new File(pathToUnZip));
//            } else {
//                String fullPath = StringUtil.trimSufffix(attachPath, File.separator) + File.separator + filePath.replace("/", File.separator);
//                FileUtil.downLoadFile(request, response, fullPath, fileName);
//            }
//        }
//    }

	/**
	 * 保存附件信息到数据库中
	 *
	 * @param sysFile     文件对象
	 * @param user        当前用户
	 * @param fileId      文件id
	 * @param oriFileName 文件名称
	 * @param mf          MultipartFile对象
	 * @param globalType  分类
	 * @param extName     扩展名
	 * @return 入库数量
	 */
	@Transactional(rollbackFor = Exception.class)
	public Map<String, Object> saveFile(Map<String, Object> dataMap, SysFile sysFile, SysUser user, String fileId,
			String oriFileName, MultipartFile mf, GlobalType globalType, String extName) {
		// 向数据库中添加数据
		sysFile.setFileId(fileId);
		// 附件名称
		String fileName = oriFileName.substring(0, oriFileName.lastIndexOf('.'));
		sysFile.setFileName(fileName);
		// 附件类型
		if (globalType != null) {
			sysFile.setTypeId(globalType.getTypeId());
			sysFile.setFileType(globalType.getTypeName());
		} else {
			sysFile.setTypeId(sysTypeKeyService.getByKey(GlobalType.CAT_FILE).getTypeId());
			sysFile.setFileType("-");
		}
		// 上传时间
		sysFile.setCreatetime(new Date());
		// 扩展名
		sysFile.setExt(extName);
		// 字节总数
		long totalBytes = mf.getSize();
		sysFile.setTotalBytes(totalBytes);
		// 说明
		sysFile.setNote(FileUtil.getSize(mf.getSize()));
		// 当前用户的信息
		if (user != null) {
			sysFile.setCreatorId(user.getUserId());
			sysFile.setCreator(user.getUsername());
			// 添加租户数据隔离
			String tenantId = ContextUtil.getCurrentUserTenantId();
			sysFile.setTenantId(tenantId);
		} else {
			sysFile.setCreator(SysFile.FILE_UPLOAD_UNKNOWN);
		}
		// 总的字节数
		sysFile.setDelFlag(SysFile.FILE_NOT_DEL);
		sysFileDao.add(sysFile);
		dataMap.put("fileId", fileId);
		dataMap.put("fileName", fileName);
		dataMap.put("fileSize", FileUtil.getSize(totalBytes));
		dataMap.put("extName", extName);
		return dataMap;
	}

	/**
	 * 获取加密文件的密码
	 *
	 * @param userAccount 上传此文件的用户登录名，来源于sys_file数据的creator字段
	 * @param fileId      拼接文件id
	 * @return 组合后的文件密码 FHBE_CASIC_用户登录名_文件id
	 */
	public static String getFilePassWord(String userAccount, String fileId) {
		return SysFile.PASSWORD_PROJECT_NAME + "_" + userAccount + "_" + fileId;
	}

	/**
	 * 配置文件中获取文件上传路径 如果为空则采用默认路径/attachFiles/temp
	 *
	 * @return 路径返回没有/或\结尾。
	 */
	public static String getBasePath() {
		String attachPath = PropertyUtil.getByAlias("file.upload");
		if (StringUtil.isEmpty(attachPath)) {
			attachPath = AppUtil.getRealPath("/attachFiles/temp");
		}
		attachPath = StringUtil.trimSufffix(attachPath, "\\");
		attachPath = StringUtil.trimSufffix(attachPath, "/");
		return attachPath;
	}

	/**
	 * 检测上传的文件是否需要加密
	 *
	 * @return true为加密，false为不加密，直接存储
	 */
	public static boolean getFileEncrypt() {
		String saveType = PropertyUtil.getByAlias("file.encrypt", "Folder");
		if (StringUtil.isEmpty(saveType)) {
			return false;
		}
		return "true".equalsIgnoreCase(saveType.trim());
	}

	/**
	 * 配置文件中获取文件存放的类型
	 *
	 * @return
	 */
	public static String getSaveType() {
		String saveType = PropertyUtil.getByAlias("file.saveType", "Folder");
		return saveType.trim().toLowerCase();
	}

	/**
	 * 获取文件流
	 *
	 * @param response 响应
	 * @param fileId   文件ID
	 * @throws Exception
	 */
	public void getFileInfo(HttpServletResponse response, String fileId) throws Exception {
		if (StringUtil.isEmpty(fileId)) {
			return;
		}
		SysFile sysFile = getOneById(fileId);
		if (sysFile == null) {
			return;
		}
		String attachPath = getBasePath().replace("/", File.separator);
		byte[] bytes;
		// 加密文件的临时解压目录，需要删除此目录
		String temp_unzipPath = "";
		// 附件保存类型
		String saveType = getSaveType();
		if (saveType.contains(SysFile.SAVETYPE_DATABASE)) {
			bytes = sysFile.getFileBlob();
		} else {
			if (SysFile.ENCRYPTED_YES == sysFile.getEncrypted()) {
				String filePath = sysFile.getFilePath();
				if (StringUtil.isEmpty(filePath)) {
					return;
				}
				if (StringUtil.isEmpty(attachPath)) {
					attachPath = AppUtil.getRealPath("/attachFiles/temp");
				}
				// 原文件路径
				String fullPath = StringUtil.trimSufffix(attachPath, File.separator) + File.separator
						+ filePath.replace("/", File.separator);
				// 暂时解压路径
				String pathToUnZip = StringUtil.trimSufffix(attachPath, File.separator) + File.separator
						+ sysFile.getFileId();
				ZipUtil.unZip(fullPath, pathToUnZip, getFilePassWord(sysFile.getCreator(), fileId));
				String fileName = sysFile.getFileId() + "." + sysFile.getExt();
				temp_unzipPath = pathToUnZip;
				bytes = FileUtil.readByte(pathToUnZip + File.separator + fileName);
			} else {
				String filePath = sysFile.getFilePath();
				// 附件保存路径
				String fullPath = StringUtil.trimSufffix(attachPath, File.separator) + File.separator
						+ filePath.replace("/", File.separator);
				bytes = FileUtil.readByte(fullPath);
			}
		}
		// 设置响应的类型格式为图片格式
		MagicMatch match = Magic.getMagicMatch(bytes);
		String mimeType = match.getMimeType();
		response.setContentType(mimeType);
		response.getOutputStream().write(bytes);
		// 如果存在临时解压目录则需要删除
		if (temp_unzipPath.length() > 0) {
			FileUtil.deleteDir(new File(temp_unzipPath));
		}
	}

	public List<SysFile> queryFileAttch(QueryFilter filter) {
		return sysFileDao.queryAllPersonalFile(filter);
	}

	/**
	 * <p>
	 * Description: 校验下载参数：因要返回给浏览器下载失败详细信息： 所有检验汇聚到这里，
	 * </p>
	 * 
	 * @Author mhc
	 * @Time 2020年6月11日 上午10:57:10
	 * @Version 1.0
	 */
	private Map<String, Object> check(String fileId) {
		Map<String, Object> result = new HashMap<String, Object>();
		result.put("code", "200");
		if (StringUtil.isEmpty(fileId)) {
			result.put("code", "400");
			result.put("message", "请传递有效参数！");
		}
		SysFile sysFile = getOneById(fileId);
		if (sysFile == null) {
			result.put("code", "400");
			result.put("message", "请传递有效参数！");
		}
		if (!this.checkLevel(sysFile.getFileLevel())) {
			result.put("code", "403");
			result.put("message", "权限不足！");
		}
		String filePath = sysFile.getFilePath();
		if (StringUtil.isEmpty(filePath)) {
			result.put("code", "400");
			result.put("message", "文件路径不存在！");
		}
		result.put("sysFile", sysFile);
		return result;

	}

	// 检验下载密级
	private boolean checkLevel(int level) {
		// 检验密级是否合法
		SysUser sysUser = (SysUser) ContextUtil.getCurrentUser();
		if (null == sysUser) {
			return false;
		}
		return SecretUtil.getUserDataSecretMap().get(sysUser.getPrivacyLevel()) >= level;
	}

	// 下载错误历史提示界面
	private void setresponse(HttpServletResponse response, String message) {
		try {
			response.setHeader("Content-Type", "text/html;charset=utf-8");
			response.setCharacterEncoding("UTF-8");
			PrintWriter writer = response.getWriter();
			writer.print("<!DOCTYPE html>\r\n" + "<html lang=\"en\">\r\n" + "<head>\r\n"
					+ "    <meta charset=\"UTF-8\">\r\n" + "    <title>Title</title>\r\n" + "    <style>\r\n"
					+ "        *{\r\n" + "            margin:0;\r\n" + "            padding:0;\r\n" + "\r\n"
					+ "        }\r\n" + "        html,body{\r\n" + "            height:100%;\r\n"
					+ "            width:100%\r\n" + "        }\r\n" + "        #show{\r\n"
					+ "            width: 300px;\r\n" + "            height: 260px;\r\n"
					+ "            background-color: #fff;\r\n" + "            position: absolute;\r\n"
					+ "            transform: translate(-50%,-50%);\r\n" + "            left: 50%;\r\n"
					+ "            top: 50%;\r\n" + "        }\r\n" + "    </style>\r\n" + "</head>\r\n" + "<body>\r\n"
					+ "<div id='main' style=\"width:100%;height:100%;opacity: 0.6;background-color:black\">\r\n"
					+ "    <div id=\"show\">\r\n"
					+ "        <div style=\"height:36px;line-height:36px;background-color: #716A6A\">\r\n"
					+ "            <span style=\"float: left;margin-left:10px;color:#fff\">响应信息</span>\r\n"
					+ "            <span id='close' style=\"float: right;margin-right: 10px;font-size: 23px;color: #fff;cursor:pointer;\">x</span>\r\n"
					+ "        </div>\r\n" + "        <div>" + message + "</div>\r\n" + "    </div>\r\n" + "</div>\r\n"
					+ "<script>\r\n" + "\r\n" + "        document.getElementById('close').onclick=function(){\r\n"
					+ "            document.getElementById('main').style.display='none'\r\n" + "        }\r\n"
					+ "</script>\r\n" + "</body>\r\n" + "</html>");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
